// ignore_for_file: non_constant_identifier_names
import 'dart:async';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:flutter/foundation.dart';
import 'package:system_proxy/system_proxy.dart';

import 'http_response.dart';

export 'http_response.dart';

extension $Dio on Dio {
  Future<bool> updateProxySetting({String? host, int? port}) async {
    if (!kIsWeb && this.httpClientAdapter is IOHttpClientAdapter) {
      String? _host = host;
      int? _port = port;
      bool _enabled = (_host != null && _port != null);
      if (!_enabled) {
        Map<String, String>? proxy = await SystemProxy.getProxySettings();
        _enabled = proxy?['host'] != null;
        if (_enabled) {
          _host = proxy!['host'];
          _port = int.parse(proxy['port']!);
        }
      }

      if (_enabled) {
        this.httpClientAdapter = IOHttpClientAdapter(
          createHttpClient: () {
            final client = HttpClient();
            client.findProxy = (uri) {
              // Proxy all request to localhost:8888.
              // Be aware, the proxy should went through you running device,
              // not the host platform.
              return "PROXY $_host:$_port";
            };
            // client.badCertificateCallback =
            //     (X509Certificate cert, String host, int port) => true;
            return client;
          },
        );
      }
    }

    return true;
  }
}

class $HttpRequest<T> {
  $HttpRequest({Dio? customDio}) {
    if (customDio != null) {
      _dio = customDio;
    } else {
      if (defaultSingleton == null) {
        defaultSingleton = setupDefaultDio();
      }
      _dio = defaultSingleton!;
    }
    baseUrl = defaultBaseUrl;
  }

  Dio setupDefaultDio() {
    Dio dio = Dio();
    dio.options.contentType = Headers.formUrlEncodedContentType;
    dio.options.responseType = ResponseType.plain;
    return dio;
  }

  static Dio? defaultSingleton;
  static late String defaultBaseUrl;
  static String defaultResponseErrorMsg = '服务器错误';
  static String defaultNetworkErrorMsg = '网络连接异常';

  late Dio _dio;
  CancelToken? cancelToken;

  late String baseUrl;
  String urlPath = '';

  /// 处理好的请求参数
  Map<String, dynamic>? workedParameters;
  Map<String, dynamic>? queryParameters;

  int leftRetryCount = 3;
  HttpMethod _method = HttpMethod.none;

  PreprocessHttpResponseBodyBlock? responsePreprocessor;
  CustomTransformHttpResponseDataBlock? dataTransformer;

  Uri get requestUri {
    assert(baseUrl.length > 0, '请求路径不合法');
    String _url = baseUrl;
    if (_url.endsWith('/') && urlPath.startsWith('/')) {
      if (urlPath.length > 1) {
        _url = _url + urlPath.substring(1);
      }
    } else if (!_url.endsWith('/') && !urlPath.startsWith('/')) {
      _url = _url + '/' + urlPath;
    } else {
      _url = _url + urlPath;
    }
    return Uri.parse(_url).replace(queryParameters: queryParameters);
  }

  void makeParameters(Map<String, dynamic> parameters,
      {needToken = true, needEncrypt = true}) {
    workedParameters = parameters;
  }

  Future<bool> updateProxySetting(Dio dio, {String? host, int? port}) async {
    return await dio.updateProxySetting(host: host, port: port);
  }

  Future<$HttpResponse<T>> sendRequest() async {
    assert(_method != HttpMethod.none, '请忽直接调用该方法');
    if (workedParameters == null) {
      makeParameters({});
    }
    // 更新Dio代理
    await updateProxySetting(_dio);

    cancelToken = CancelToken();
    Completer completer = Completer();

    Future(() async {
      try {
        Response httpResponse = await _dio.requestUri(
          requestUri,
          data: workedParameters,
          cancelToken: cancelToken,
          options: Options(
            method: _httpMethods[_method],
          ),
        );

        final result = await requestWillSucceedCallback(httpResponse);
        completer.complete(result);
      } catch (e) {
        debugPrint(e.toString());
        var errorResult;
        if (e is DioError) {
          if (e.type == DioErrorType.unknown) {
            errorResult = await requestWillFailCallback(
                $HttpErrorCode.network, defaultNetworkErrorMsg);
          } else {
            errorResult = await requestWillFailCallback(
                $HttpErrorCode.response, defaultResponseErrorMsg);
          }
        }

        if (errorResult == null) {
          bool debug = false;
          assert(() {
            debug = true;
            return true;
          }());
          if (debug) {
            rethrow;
          }
          errorResult =
              await requestWillFailCallback($HttpErrorCode.unknown, '请求出错');
        }

        if (errorResult != null) {
          completer.complete(errorResult);
        }
      }
    });

    $HttpResponse<T> response = await completer.future;

    return response;
  }

  Future<$HttpResponse<T>> requestWillSucceedCallback(
      Response responseObject) async {
    $HttpResponse<T> result = await $HttpResponse.fromResponse<T>(
        responseObject, responsePreprocessor, dataTransformer);
    return result;
  }

  Future<$HttpResponse> requestWillFailCallback(
      int errorCode, String errorMsg) async {
    $HttpResponse<T> errorResult =
        $HttpResponse<T>.errorResponse(errorCode: errorCode, message: errorMsg);
    return errorResult;
  }

  Future POST() async {
    _method = HttpMethod.post;
    return sendRequest();
  }

  Future GET() async {
    _method = HttpMethod.get;
    return sendRequest();
  }
}

const Map<HttpMethod, String> _httpMethods = {
  HttpMethod.get: 'GET',
  HttpMethod.post: 'POST',
};
